﻿using System;
using System.Linq;
using System.Data.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;
using CSW.Framework.Common;
using CSW.Framework.UI.Binding;

namespace CSW.Framework.UI
{
    /// <summary>
    /// Typed Data Grid Controller.  Type safe access to <see cref="DataGrid" />.
    /// </summary>
    /// <typeparam name="T">The type implementing <see cref="INotifyPropertyChanged" />.</typeparam>
    public class TypedDataGridViewController<T> //: ITypedGridController<T>
        where T : class, INotifyPropertyChanged
    {
        private readonly DataGridView m_DataGridView;

        /// <summary>
        /// Initializes a new instance of the <see cref="TypedDataGridViewController{TSearchItem}"/> class.
        /// </summary>
        /// <param name="dataGridView">The grid control.</param>
        public TypedDataGridViewController(DataGridView dataGridView)
            : this(dataGridView, true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TypedDataGridViewController{T}"/> class.
        /// </summary>
        /// <param name="dataGridView">The grid control.</param>
        /// <param name="multiSelect">if set to <c>true</c>, multi select is enabled (default = true).</param>
        public TypedDataGridViewController(DataGridView dataGridView, bool multiSelect)
        {
            Guard.ArgumentNotNull(dataGridView, "gridControl");

            m_DataGridView = dataGridView;

            m_DataGridView.MultiSelect = multiSelect;

            m_DataGridView.AllowUserToAddRows = false;
            m_DataGridView.AllowUserToDeleteRows = false;
            m_DataGridView.ReadOnly = true;
            m_DataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;

            m_DataGridView.AllowUserToOrderColumns = true;
            m_DataGridView.AllowUserToResizeColumns = true;
            m_DataGridView.AllowUserToResizeRows = false;
            m_DataGridView.AutoGenerateColumns = false;
            m_DataGridView.TabStop = false;

            m_DataGridView.CurrentCellDirtyStateChanged += m_DataGridView_CurrentCellDirtyStateChanged;
        }

        /// <summary>
        /// Gets the selected object if one and only one row is selected; otherwise, returns <c>null</c>.
        /// </summary>
        /// <returns>The selected object if one and only one row is selected; otherwise, returns <c>null</c>.</returns>
        public T GetSelectedObject()
        {
            var selectedRows = m_DataGridView.SelectedRows;
            if (selectedRows.Count != 1)
            {
                if (m_DataGridView.CurrentRow != null)
                {
                    return (T)m_DataGridView.CurrentRow.DataBoundItem;
                }

                return null;
            }

            return (T)selectedRows[0].DataBoundItem;
        }

        /// <summary>
        /// Gets the selected objects.
        /// </summary>
        /// <returns>The selected objects.</returns>
        public List<T> GetSelectedObjects()
        {
            DataGridViewSelectedRowCollection selectedRows = m_DataGridView.SelectedRows;
            if (selectedRows.Count == 0)
                return null;

            return selectedRows.Cast<DataGridViewRow>()
                   .Select(p => (T)p.DataBoundItem)
                   .ToList();
        }

        /// <summary>
        /// Selects a single row.
        /// </summary>
        /// <param name="item">The item.</param>
        public void SelectSingleRow(T item)
        {
            Guard.ArgumentNotNull(item, "item");

            m_DataGridView.ClearSelection();
            DataGridViewRow rowItem = m_DataGridView.Rows.Cast<DataGridViewRow>()
                                      .Where(p => p.DataBoundItem == item)
                                      .Single();
            rowItem.Selected = true;
            m_DataGridView.CurrentCell = rowItem.Cells[0];
        }

        /// <summary>
        /// Adds a field.
        /// </summary>
        /// <param name="caption">The caption.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <returns>The newly created <see cref="DataGridViewColumn" />.</returns>
        public DataGridViewColumn AddField(string caption, string fieldName)
        {
            return AddField(caption, fieldName, null, 65);
        }

        /// <summary>
        /// Adds a field.
        /// </summary>
        /// <param name="caption">The caption.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="dataGridViewColumn">The <see cref="DataGridViewColumn"/>.</param>
        /// <returns>The newly created <see cref="DataGridViewColumn" />.</returns>
        public DataGridViewColumn AddField(string caption, string fieldName, DataGridViewColumn dataGridViewColumn)
        {
            return AddField(caption, fieldName, null, 65);
        }

        /// <summary>
        /// Adds a field.
        /// </summary>
        /// <param name="caption">The caption.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="width">The width.</param>
        /// <returns>The newly created <see cref="DataGridViewColumn" />.</returns>
        public DataGridViewColumn AddField(string caption, string fieldName, int width)
        {
            return AddField(caption, fieldName, null, width);
        }

        /// <summary>
        /// Adds a field.
        /// </summary>
        /// <param name="caption">The caption.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="formatString">The format string.</param>
        /// <returns>The newly created <see cref="DataGridViewColumn" />.</returns>
        public DataGridViewColumn AddField(string caption, string fieldName, string formatString)
        {
            return AddField(caption, fieldName, formatString, 65);
        }

        /// <summary>
        /// Adds a field.
        /// </summary>
        /// <param name="caption">The caption.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="formatString">The format string.</param>
        /// <param name="width">The width.</param>
        /// <returns>The newly created <see cref="DataGridViewColumn" />.</returns>
        public DataGridViewColumn AddField(string caption, string fieldName, string formatString, int width)
        {
            return AddField(caption, fieldName, formatString, width, null);
        }

        /// <summary>
        /// Adds a field.
        /// </summary>
        /// <param name="caption">The caption.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="formatString">The format string.</param>
        /// <param name="width">The width.</param>
        /// <param name="dataGridViewColumn">The <see cref="DataGridViewColumn"/>.</param>
        /// <returns>The newly created <see cref="DataGridViewColumn" />.</returns>
        public DataGridViewColumn AddField(string caption, string fieldName, string formatString, int width, DataGridViewColumn dataGridViewColumn)
        {
            //bool isPrimitive = true;
            PropertyInfo propertyInfo = typeof(T).GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance);
            if (propertyInfo != null)
            {
                if (propertyInfo.PropertyType == typeof(bool) ||
                    propertyInfo.PropertyType == typeof(bool?))
                {
                    if (dataGridViewColumn == null)
                    {
                        dataGridViewColumn = new DataGridViewCheckBoxColumn(false);
                    }

                    /*if (repositoryItem == null)
                    {
                        repositoryItem = new RepositoryItemCheckEdit();
                        m_DataGridView.RepositoryItems.Add(repositoryItem);
                        repositoryItem.AutoHeight = false;
                        repositoryItem.Name = "repositoryItemCheckEdit" + fieldName;
                        repositoryItem.EditValueChanged += delegate { m_GridView.CloseEditor(); };
                    }*/
                }
                else if (propertyInfo.PropertyType == typeof(DateTime) ||
                         propertyInfo.PropertyType == typeof(DateTime?))
                {
                    if (dataGridViewColumn == null)
                    {
                        //dataGridViewColumn = new DataGridViewT
                    }
                }
                else if (!propertyInfo.PropertyType.IsPrimitive &&
                          propertyInfo.PropertyType != typeof(string) &&
                          propertyInfo.PropertyType != typeof(decimal) &&
                          propertyInfo.PropertyType != typeof(decimal?))
                {
                    //isPrimitive = false;
                }
            }

            if (dataGridViewColumn == null)
            {
                dataGridViewColumn = new DataGridViewTextBoxColumn();
            }

            dataGridViewColumn.HeaderText = caption;
            dataGridViewColumn.Name = fieldName;
            dataGridViewColumn.DataPropertyName = fieldName;
            dataGridViewColumn.Visible = true;
            dataGridViewColumn.DisplayIndex = m_DataGridView.Columns.Count;
            dataGridViewColumn.SortMode = DataGridViewColumnSortMode.Automatic;

            if (!string.IsNullOrEmpty(formatString))
            {
                dataGridViewColumn.DefaultCellStyle.Format = formatString;
                //gridColumn.DisplayFormat.FormatType = FormatType.Custom;
            }
            dataGridViewColumn.Width = width;
            m_DataGridView.Columns.Add(dataGridViewColumn);

            return dataGridViewColumn;
        }

        private void m_DataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            DataGridViewCell cell = m_DataGridView.CurrentCell;
            if (cell != null)
            {
                DataGridViewColumn column = m_DataGridView.Columns[cell.ColumnIndex];
                if (column.GetType() == typeof(DataGridViewCheckBoxColumn))
                {
                    m_DataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
                }
            }
        }

        /// <summary>
        /// Gets or sets the data source.
        /// </summary>
        /// <value>The data source.</value>
        public BindingList<T> DataSource
        {
            get { return (BindingList<T>)m_DataGridView.DataSource; }
            set { m_DataGridView.DataSource = value; }
        }

        /// <summary>
        /// Sets the data source.
        /// </summary>
        public EntitySet<T> DataEntitySet
        {
            set
            {
                if (value != null)
                    m_DataGridView.DataSource = value.GetNewBindingList();
                else
                    m_DataGridView.DataSource = null;
            }
        }

        /// <summary>
        /// Best fits the columns.
        /// </summary>
        public void BestFitColumns()
        {
            m_DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
        }

        /// <summary>
        /// Clears the selection.
        /// </summary>
        public void ClearSelection()
        {
            m_DataGridView.ClearSelection();
        }

        /// <summary>
        /// Gets the row count.
        /// </summary>
        /// <value>The row count.</value>
        public int RowCount
        {
            get { return m_DataGridView.RowCount; }
        }

        /// <summary>
        /// Gets the <see cref="DataGridView" />.
        /// </summary>
        /// <value>The <see cref="DataGridView" />.</value>
        public DataGridView DataGridView
        {
            get { return m_DataGridView; }
        }

        /// <summary>
        /// Searches using the specified search method.
        /// </summary>
        /// <param name="searchMethod">The search method.</param>
        public void SetSearchResults(Func<BindingList<T>> searchMethod)
        {
            Guard.ArgumentNotNull(searchMethod, "searchMethod");

            Cursor.Current = Cursors.WaitCursor;
            BindingList<T> results = searchMethod();
            if (results == null || results.Count == 0)
                MessageBox.Show("No records found.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
            else
                DataSource = results;
            Cursor.Current = Cursors.Default;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public virtual void Dispose()
        {
            m_DataGridView.CurrentCellDirtyStateChanged -= m_DataGridView_CurrentCellDirtyStateChanged;
        }
    }
}
